package com.pkk.components.config;

import ch.qos.logback.classic.AsyncAppender;
import ch.qos.logback.classic.Level;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.LoggerContextListener;
import ch.qos.logback.core.spi.ContextAwareBase;
import java.net.InetSocketAddress;
import net.logstash.logback.appender.LogstashTcpSocketAppender;
import net.logstash.logback.encoder.LogstashEncoder;
import net.logstash.logback.stacktrace.ShortenedThrowableConverter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;

/**
 * @description:
 * @author: peikunkun
 * @create: 2018-10-29 17:53
 **/
@Configuration
public class LoggingConfiguration {

  private static final Logger log = LoggerFactory.getLogger(LoggingConfiguration.class);
  private static final String LOGSTASH_APPENDER_NAME = "LOGSTASH";
  private static final String ASYNC_LOGSTASH_APPENDER_NAME = "ASYNC_LOGSTASH";
  private final String appName;
  private final String serverPort;
  private final String activeProfile;
  private final LoggingProperties loggingProperties;
  private LoggerContext context = (LoggerContext) LoggerFactory.getILoggerFactory();

  public LoggingConfiguration(Environment environment, @Value("${spring.application.name}") String appName,
      LoggingProperties loggingProperties, @Value("${spring.profiles.active}") String activeProfile) {
    this.appName = appName;
    this.serverPort = environment.getProperty("local.server.port");
    this.loggingProperties = loggingProperties;
    this.activeProfile = activeProfile;
    if (loggingProperties.isEnabled()) {
      this.addLogstashAppender(this.context);
      this.addContextListener(this.context);
    }

  }

  private void addContextListener(LoggerContext context) {
    LoggingConfiguration.LogbackLoggerContextListener loggerContextListener = new LoggingConfiguration.LogbackLoggerContextListener();
    loggerContextListener.setContext(context);
    context.addListener(loggerContextListener);
  }

  private void addLogstashAppender(LoggerContext context) {
    log.info("Initializing Logstash logging");
    LogstashTcpSocketAppender logstashAppender = new LogstashTcpSocketAppender();
    logstashAppender.setName("LOGSTASH");
    logstashAppender.setContext(context);
    String customFields =
        "{\"app_name\":\"" + this.appName + "-" + this.activeProfile + "\",\"app_port\":\"" + this.serverPort + "\"}";
    LogstashEncoder logstashEncoder = new LogstashEncoder();
    logstashEncoder.setCustomFields(customFields);
    logstashAppender.addDestinations(new InetSocketAddress[]{
        new InetSocketAddress(this.loggingProperties.getHost(), this.loggingProperties.getPort())});
    ShortenedThrowableConverter throwableConverter = new ShortenedThrowableConverter();
    throwableConverter.setRootCauseFirst(true);
    logstashEncoder.setThrowableConverter(throwableConverter);
    logstashEncoder.setCustomFields(customFields);
    logstashAppender.setEncoder(logstashEncoder);
    logstashAppender.start();
    AsyncAppender asyncLogstashAppender = new AsyncAppender();
    asyncLogstashAppender.setContext(context);
    asyncLogstashAppender.setName("ASYNC_LOGSTASH");
    asyncLogstashAppender.setQueueSize(this.loggingProperties.getQueueSize());
    asyncLogstashAppender.addAppender(logstashAppender);
    asyncLogstashAppender.start();
    context.getLogger("ROOT").addAppender(asyncLogstashAppender);
  }

  class LogbackLoggerContextListener extends ContextAwareBase implements LoggerContextListener {

    LogbackLoggerContextListener() {
    }

    @Override
    public boolean isResetResistant() {
      return true;
    }

    @Override
    public void onStart(LoggerContext context) {
      LoggingConfiguration.this.addLogstashAppender(context);
    }

    @Override
    public void onReset(LoggerContext context) {
      LoggingConfiguration.this.addLogstashAppender(context);
    }

    @Override
    public void onStop(LoggerContext context) {
    }

    @Override
    public void onLevelChange(ch.qos.logback.classic.Logger logger, Level level) {
    }
  }
}